/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.cef.core.manager;

import com.inspur.edp.cef.api.RefObject;
import com.inspur.edp.cef.api.dataType.base.IAccessorCreator;
import com.inspur.edp.cef.api.message.CefException;
import com.inspur.edp.cef.entity.accessor.entity.AccessorCollection;
import com.inspur.edp.cef.entity.accessor.entity.IChildAccessor;
import com.inspur.edp.cef.entity.accessor.entity.IEntityAccessor;
import com.inspur.edp.cef.entity.accessor.entity.IRootAccessor;
import com.inspur.edp.cef.entity.changeset.IChangeDetail;
import com.inspur.edp.cef.entity.changeset.InnerUtil;
import com.inspur.edp.cef.entity.changeset.ModifyChangeDetail;
import com.inspur.edp.cef.entity.dependenceTemp.DataValidator;
import com.inspur.edp.cef.entity.entity.ICefData;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.entity.entity.IEntityDataCollection;
import com.inspur.edp.cef.entity.entity.IMultiLanguageData;

import java.util.Iterator;
import java.util.Map;

public class EntityDataManager
{
	public static EntityDataManager getInstance()
	{
		return new EntityDataManager();
	}

	public final void acceptChanges(IAccessorCreator accessorCreator, IEntityData source, RefObject<IEntityData> target, IChangeDetail sourceChangeDetail) //, IChangeDetail targetChangeDetail
	{
		if (sourceChangeDetail == null)
		{
			return;
		}

		if ((source != null && (source instanceof IRootAccessor) == false) || (target.argvalue != null && (target.argvalue instanceof IRootAccessor) == false))
		{
			throw new CefException("框架级别错误，请联系平台开发处理");
		}

		IRootAccessor sourceAccessor = (IRootAccessor)((source instanceof IRootAccessor) ? source : null);
		IRootAccessor targetAccessor = (IRootAccessor)((target.argvalue instanceof IRootAccessor) ? target.argvalue : null);
		switch (sourceChangeDetail.getChangeType())
		{
			case Added:
				if(source == null)
				{
					return;
				}
				RefObject<IRootAccessor> tempRef_targetAccessor = new RefObject<IRootAccessor>(targetAccessor);
				acceptChangesByAdd(accessorCreator, sourceAccessor, tempRef_targetAccessor);
				targetAccessor = tempRef_targetAccessor.argvalue;
				break;
			case Modify:
				if(source == null)
				{
					return;
				}
				RefObject<IRootAccessor> tempRef_targetAccessor2 = new RefObject<IRootAccessor>(targetAccessor);
				acceptChangesByModify(sourceAccessor, tempRef_targetAccessor2, (ModifyChangeDetail) sourceChangeDetail);
				targetAccessor = tempRef_targetAccessor2.argvalue;
				break;
			case Deleted:
				RefObject<IRootAccessor> tempRef_targetAccessor3 = new RefObject<IRootAccessor>(targetAccessor);
				acceptChangesByDelete(sourceAccessor, tempRef_targetAccessor3);
				targetAccessor = tempRef_targetAccessor3.argvalue;
				break;
			default:
				throw new RuntimeException();
		}
		target.argvalue = targetAccessor;
	}

	private void acceptChangesByAdd(IAccessorCreator accessorCreator, IRootAccessor source, RefObject<IRootAccessor> target)
	{
		target.argvalue = (IRootAccessor)accessorCreator.createReadonlyAccessor(source.copySelf());
		if(source instanceof IMultiLanguageData){
			IMultiLanguageData sourceData = (IMultiLanguageData)source;
			IMultiLanguageData targetData = (IMultiLanguageData)target.argvalue;
			InnerUtil.resetMultiLangInfo(sourceData, targetData);
		}
		acceptChilds(source, target.argvalue);
	}

	//current source => transaction target
	private static void acceptAccessorByModifyChange(IEntityAccessor source, IEntityAccessor target,ModifyChangeDetail modifyChangeDetail) {
		//todo 这个地方，抽空改成直接taget.acceptchange();
		if(modifyChangeDetail.getPropertyChanges()!=null&&modifyChangeDetail.getPropertyChanges().size()>0) {
			ICefData entityDef = source.copySelf();
			target.setInnerData(entityDef);
		}
		if(source instanceof IMultiLanguageData){
			InnerUtil.resetMultiLangInfo((IMultiLanguageData)source, (IMultiLanguageData)target);
		}
		//target.Version = entityDef.Version = targetVersion;
		acceptChildsByModifyChange(source, target,modifyChangeDetail);
	}

	private static void acceptChildsByModifyChange(IEntityAccessor source, IEntityAccessor target,ModifyChangeDetail modifyChangeDetail)
	{
		if(modifyChangeDetail.getChildChanges()==null||modifyChangeDetail.getChildChanges().size()==0) {
			return;
		}
		java.util.Map<String, IEntityDataCollection> childs = source.getChilds();
		if (childs == null)
		{
			return;
		}
		java.util.Map<String, IEntityDataCollection> targeChilds = target.getChilds();
		for (Map.Entry<String, Map<String, IChangeDetail>> childChanges :modifyChangeDetail.getChildChanges().entrySet())
		{
			if(childChanges.getValue()==null||childChanges.getValue().size()==0) {
				continue;
			}
			IEntityDataCollection childItems = childs.get(childChanges.getKey());
			AccessorCollection targetChildCollection = null;
			if(targeChilds.containsKey(childChanges.getKey())) {
				targetChildCollection = (AccessorCollection) targeChilds.get(childChanges.getKey());
			}
			else {
				targetChildCollection = (AccessorCollection)target.createAndSetChildCollection(childChanges.getKey());
			}
			for(Map.Entry<String,IChangeDetail> childChange:childChanges.getValue().entrySet())
			{
				switch (childChange.getValue().getChangeType())
				{
					case Added: {
						ICefData sourceChildData = childItems.getItem(childChange.getKey());
						IChildAccessor accessor = targetChildCollection.createInstance();
						accessor.setInnerData(sourceChildData.copySelf());
						targetChildCollection.innerAdd(accessor);
						acceptChilds((IEntityAccessor) sourceChildData, accessor);
					}
						break;
					case Modify: {
						ModifyChangeDetail childModifyChangeDetail = (ModifyChangeDetail) childChange.getValue();
						IEntityAccessor sourceChildData = (IEntityAccessor) childItems.getItem(childChange.getKey());
						IChildAccessor accessor = (IChildAccessor) targetChildCollection.getItem(childChange.getKey());
						acceptAccessorByModifyChange(sourceChildData,accessor,childModifyChangeDetail);
					}
						break;
					case Deleted:
						targetChildCollection.innerDelete(childChange.getKey());
						break;
						default:
							throw new RuntimeException("暂时不支持变更类型："+childChange.getValue().getChangeType());
				}
			}
		}
	}

	private static void acceptChilds(IEntityAccessor source, IEntityAccessor target)
	{
		java.util.Map<String, IEntityDataCollection> childs = source.getChilds();
		if (childs == null)
		{
			return;
		}
		for (Map.Entry<String,IEntityDataCollection> item : childs.entrySet())
		{
			//if (item.Value == null || item.Value.Count == 0)
			//    continue;
			AccessorCollection targetChildCollection = (AccessorCollection)target.createAndSetChildCollection(item.getKey());
			if (item.getValue() != null)
			{
				importDatas(targetChildCollection, item.getValue());
			}

			for (IEntityData targetItem : targetChildCollection)
			{
				IEntityData data=item.getValue().getItem(targetItem.getID());
				acceptChilds((IEntityAccessor)data, (IEntityAccessor)targetItem);
			}
		}
	}

	private static void importDatas(AccessorCollection collection, IEntityDataCollection source)
	{
		if (source == null)
		{
			return;
		}
		Iterator<IEntityData> iterator= source.iterator();
		while (iterator.hasNext())
		{
			IChildAccessor accessor = collection.createInstance();
			accessor.setInnerData( iterator.next().copySelf());
			collection.innerAdd(accessor);
		}
	}

	private void acceptChangesByModify(IRootAccessor source, RefObject<IRootAccessor> target, ModifyChangeDetail modifyChangeDetail)
	{
		DataValidator.checkForNullReference(target.argvalue, "target");
		acceptAccessorByModifyChange(source, target.argvalue,modifyChangeDetail);
	}

	private void acceptChangesByDelete(IRootAccessor source, RefObject<IRootAccessor> target)
	{
		target.argvalue = null;
	}

	public final void rejectChanges(IAccessorCreator accessorCreator, RefObject<IEntityData> source, IEntityData target, IChangeDetail sourceChangeDetail, boolean isSourceReadonly)
	{
		if (sourceChangeDetail == null)
		{
			return;
		}
		if ((source.argvalue != null && (source.argvalue instanceof IRootAccessor) == false) || (target != null && (target instanceof IRootAccessor) == false))
		{
			throw new RuntimeException();
		}

		IRootAccessor sourceAccessor = (IRootAccessor)((source.argvalue instanceof IRootAccessor) ? source.argvalue : null);
		IRootAccessor targetAccessor = (IRootAccessor)((target instanceof IRootAccessor) ? target : null);
		switch (sourceChangeDetail.getChangeType())
		{
			case Added:
				if (source.argvalue == null)
				{
					return;
				}
				RefObject<IRootAccessor> tempRef_sourceAccessor = new RefObject<IRootAccessor>(sourceAccessor);
				rejectAddedChanges(tempRef_sourceAccessor);
				sourceAccessor = tempRef_sourceAccessor.argvalue;
				break;
			case Modify:
				if (source.argvalue == null)
				{
					return;
				}
				RefObject<IRootAccessor> tempRef_sourceAccessor2 = new RefObject<IRootAccessor>(sourceAccessor);
				rejectModifiedChanges(accessorCreator, tempRef_sourceAccessor2, targetAccessor, isSourceReadonly);
				sourceAccessor = tempRef_sourceAccessor2.argvalue;
				break;
			case Deleted:
				RefObject<IRootAccessor> tempRef_sourceAccessor3 = new RefObject<IRootAccessor>(sourceAccessor);
				rejectDeletedChanges(accessorCreator, tempRef_sourceAccessor3, targetAccessor, isSourceReadonly);
				sourceAccessor = tempRef_sourceAccessor3.argvalue;
				break;
			default:
				throw new RuntimeException();
		}
		source.argvalue = sourceAccessor;
	}

	private void rejectAddedChanges(RefObject<IRootAccessor> source)
	{
		source.argvalue = null;
	}

	private void rejectModifiedChanges(IAccessorCreator accessorCreator, RefObject<IRootAccessor> source, IRootAccessor target, boolean isSourceReadonly)
	{
		rejectAccessor(source.argvalue, target);
	}

	//source current => target transaction
	private void rejectAccessor(IEntityAccessor source, IEntityAccessor target)
	{
		source.setInnerData(target);
		java.util.Map<String, IEntityDataCollection> childs = target.getChilds();
		if (childs == null)
		{
			return;
		}
		for (Map.Entry<String, IEntityDataCollection> item : childs.entrySet())
		{
			AccessorCollection sourceChildCollection = (AccessorCollection)source.createAndSetChildCollection(item.getKey());
			if (item.getValue() != null)
			{
				importDatas(sourceChildCollection, item.getValue());
			}
			for (IEntityData sourceItem : sourceChildCollection)
			{
				rejectAccessor((IEntityAccessor)sourceItem, (IEntityAccessor)item.getValue().getItem(sourceItem.getID()));
			}
		}
	}

	private void rejectDeletedChanges(IAccessorCreator accessorCreator, RefObject<IRootAccessor> source, IRootAccessor target, boolean isSourceReadonly)
	{
		if (isSourceReadonly)
		{
			source.argvalue = (IRootAccessor)accessorCreator.createReadonlyAccessor(null);
		}
		else
		{
			source.argvalue = (IRootAccessor)accessorCreator.createAccessor(null);
		}
		rejectAccessor(source.argvalue, target);
	}
}
